package utils

import (
	"fmt"
	"math"
	"math/cmplx"
)

type Convert struct {
	phase float64
	omg   float64
}

func NewConvert(f float64) *Convert {
	return &Convert{0.0, f * 2.0 * math.Pi}
}

func (c *Convert) SetF(f float64) {
	c.omg = f * 2. * math.Pi
}

func (c *Convert) Done(din []complex128) (dout []complex128, err error) {
	dout = make([]complex128, len(din))
	for i, v := range din {
		dout[i] = v * cmplx.Exp(complex(0.0, c.phase))
		c.phase += c.omg
		if c.phase > math.Pi {
			c.phase -= 2. * math.Pi
		} else if c.phase < -math.Pi {
			c.phase += 2. * math.Pi
		}
	}
	return
}

type CIC struct {
	order int
	sum   []complex128
	dif   []complex128
	dic   int
	c     int
}

func NewCIC(order, dic int) *CIC {
	r := CIC{}
	r.order = order
	r.sum = make([]complex128, order)
	r.dif = make([]complex128, order)
	r.dic = dic
	return &r
}

func (cic *CIC) Done(din []complex128) (dout []complex128, err error) {
	sc := cic.c
	ps := 0
	dl := cic.c + len(din)
	os := dl / cic.dic
	cic.c %= cic.dic
	normal := 1.0
	for range cic.sum {
		normal /= float64(cic.dic)
	}
	dout = make([]complex128, os)
	for _, v := range din {
		sx := v
		for s := range cic.sum {
			sx += cic.sum[s]
			cic.sum[s] = sx
		}
		sc += 1
		if sc == cic.dic {
			for s := range cic.dif {
				sx, cic.dif[s] = sx-cic.dif[s], sx
			}
			dout[ps] = sx * complex(normal, 0.)
			sc = 0
			ps += 1
		}
	}

	if ps != os || sc != cic.c {
		err = fmt.Errorf("size calc error: %d!=%d,%d!=%d", ps, os, sc, cic.c)
	}

	// log.Info(cic.dif, cic.sum, dout[:4])
	return
}

type Filter struct {
	coeff []float64
	buf   []complex128
}

func (f *Filter) Done(din []complex128) (dout []complex128, err error) {
	dout = make([]complex128, len(din))
	l := len(f.coeff)
	for i, v := range din {
		copy(f.buf[:l-1], f.buf[1:])
		f.buf[l-1] = v
		dout[i] += NpDotCF(f.buf, f.coeff)
	}
	return
}

type AGC struct {
	gain   float64
	err    float64
	target float64
}

func NewAGC(t float64) *AGC {
	return &AGC{1., 0., t}
}

func limit(x complex128) complex128 {
	if cmplx.Abs(x) > 1. {
		a := cmplx.Abs(x)
		return x / complex(a, 0.)
	} else {
		return x
	}
}

func (g *AGC) Done(din []complex128) (dout []complex128, err error) {
	dout = make([]complex128, len(din))
	pow := 0.0
	for i, v := range din {
		dout[i] = limit(complex(g.gain, 0.) * (v))
		pow += real(dout[i] * cmplx.Conj(dout[i]))
	}
	if g.target*g.target < pow/float64(len(dout)) {
		g.gain *= 0.99
	} else {
		g.gain *= 1.01
	}
	// log.Info(g.gain, g.err, dout[:4], din[:100])
	return
}

type DCRemover struct {
	dc complex128
	df func(float64)
}

func NewDC() *DCRemover {
	return &DCRemover{}
}
func (d *DCRemover) SetDf(df func(float64)) { d.df = df }

func (r *DCRemover) Done(din []complex128) (dout []complex128, err error) {
	dout = make([]complex128, len(din))
	sum := 0.0 + 0.i
	isum := 0.0 + 0.i
	for i, v := range din {
		isum += v
		dout[i] = v - r.dc
		sum += dout[i]
	}
	sum /= complex(float64(len(din)), 0.)
	r.dc += 0.1 * sum
	if r.df != nil {
		r.df(real(isum) / float64(len(din)) / 2. / math.Pi)
	}
	return
}
